#include "GameScene.h"

//Include scene manager so we can transition to a new
//scene (demo scene)
#include "SceneManager.h"

//Complete forward declarations
#include "SkyDome.h"

//Adv Models:
#include "Terrain.h" 

//Roo's world specific
#include "ObjectManager.h"
#include "Player.h"
#include "InstancedOrbs.h" 
#include "House.h"
#include "QuestGiver.h"
#include "GameHUD.h"


GameScene::GameScene()
{
	//Models - init to NULL within the constructor. Memory should be allocated
	//in the OpenScene() function and memroy deleted in CloseScene().  
	terrain = 0;
	skyDome = 0;

	//2D Sprite rendering and font.
	font = 0;
	spriteBatch = 0;
	tex2D = 0;

	//Roo's world specific stuff  
	playerModel = 0;
	orb = 0;
	houseModels = 0;
	NPCModel = 0;
	HUD = 0;
}

GameScene::~GameScene()
{}

void GameScene::OpenScene()
{
	//Seed random number generator to 0 - thus, each time we run the game, the 
	//point/spot lights will be located and coloured exactly the same. 
	srand(0);

	//Set MSAA State
#ifdef GAME_SCENE_ENABLE_MSAA
	sharedGraphicsInterface->Set4XMSAA(true);
#else
	sharedGraphicsInterface->Set4XMSAA(false);
#endif

	//Init camera position / frustum properties.
	camera->SetPosition(XMFLOAT3(0.0f, 120.0f, 0.0f));
	camera->RebuildViewMatrix();
	OnResize();

	//Load the skydome FIRST
	LoadSkyDome();

	//Load terrain SECOND
	LoadTerrain();

	//Load Models (After skydome)
	LoadModels();

	//Init lights
	InitLighting();
	InitDirecLight();
	InitHemisphericalAmbientLight();

	//Initial Scene State is to draw scene as usual.
	sceneState = SCENE_STATE_DRAW_SCENE;
	//Lights initially to be rendered
	shouldRenderLights = true;
	//Free camera initially.
	useFreeCamera = true;

	//We want shadow mapping
	sharedGraphicsInterface->SetIsShadowMappingEnabled(true);
}

void GameScene::CloseScene()
{
	//Delete models
	SafeDelete(terrain);
	SafeDelete(skyDome);

	//Roo's world specific
	ObjectManager::GetInstance()->ReleaseObjects();
	ObjectManager::GetInstance()->Release();

	SafeDelete(houseModels);
}

void GameScene::OnSceneReset()
{
	//Set MSAA State
#ifdef GAME_SCENE_ENABLE_MSAA
	sharedGraphicsInterface->Set4XMSAA(true);
#else
	sharedGraphicsInterface->Set4XMSAA(false);
#endif

	//Reset skydome...
	sharedGraphicsInterface->SetCurrentSkyDome(skyDome);

	//Reset lighting. 
	srand(0); //Reset random num generator for light locations, colour etc.

	//Reset all lighting. 
	InitLighting();
	InitDirecLight();
	InitHemisphericalAmbientLight();

	//We want shadow mapping
	sharedGraphicsInterface->SetIsShadowMappingEnabled(true);
}

void GameScene::LoadModels()
{

	//loads all objects for this scene
	ObjectManager::GetInstance()->Intialise(terrain,camera,sharedGraphicsInterface);

	//Loads teleporting houses
	LoadHouseModel();
}

void GameScene::LoadHUD()
{
	HUD = new GameHUD();

	// Load the 2D images and fonts from the HUD class
	HUD->Initalise(sharedGraphicsInterface->GetDevice());
}

void GameScene::LoadTerrain()
{
	//Create terrain 
	terrain = new Terrain();
	terrain->Init(sharedGraphicsInterface->GetDevice(), skyDome);
}

void GameScene::LoadSkyDome()
{
	skyDome = new SkyDome(sharedGraphicsInterface->GetDevice(),
		//L"Terrain/QuantumTheory/QuantumHighlands/Textures/SkyDome/cubeMap.dds", 
		L"Textures/snowcube1024.dds",
		camera);
	sharedGraphicsInterface->SetCurrentSkyDome(skyDome);
}

void GameScene::LoadFontData()
{
	//Load fonts. 
	font = new FontSheet();
	spriteBatch = new SpriteBatch();

	HR(font->Initialize(sharedGraphicsInterface->GetDevice(), 
		L"Times New Roman", 96.0f, FontSheet::FontStyleItalic, true));
	HR(spriteBatch->Initialize(sharedGraphicsInterface->GetDevice()));

	//Load 2D texture
	HR(D3DX11CreateShaderResourceViewFromFile(sharedGraphicsInterface->GetDevice(), 
		L"Textures/stones.dds", 0, 0, &tex2D,0));
}

void GameScene::LoadCollectables()
{
	//Create orb
	orb = new InstancedOrbs(sharedGraphicsInterface->GetDevice(), GAME_SCENE_INITIAL_NUM_ORBS);
	
	//Set orb locations
	std::vector<XMFLOAT3>orbLocations(GAME_SCENE_INITIAL_NUM_ORBS);
	orbLocations[0] = XMFLOAT3(50.0f, terrain->GetHeight(50.0f, 200.0f), 200.0f);
	orbLocations[1] = XMFLOAT3(10.0f, terrain->GetHeight(10.0f, 50.0f), 50.0f);

	//Pass locations to the InstacedOrb class to init.
	orb->SetOrbLocations(orbLocations);
}

void GameScene::LoadHouseModel()
{
	//Alloc and create house model
	houseModels = new House();
	houseModels->Init(sharedGraphicsInterface->GetDevice(), GAME_SCENE_NUM_HOUSES);

	//Create instances
	XMFLOAT3 houseScale = XMFLOAT3(0.04f, 0.04f, 0.04f);

	houseModels->CreateInstance(XMFLOAT3(600.0f, terrain->GetHeight(600.0f, 200.0f), 200.0f), 
		XMFLOAT3(0.0f, 0.0f, 0.0f), houseScale, 
		"House Demo Scene One");

	houseModels->CreateInstance(XMFLOAT3(1200.0f, terrain->GetHeight(1200.0f, 200.0f), 200.0f), 
		XMFLOAT3(0.0f, 0.0f, 0.0f), houseScale, 
		"House Demo Scene Two");

	houseModels->CreateInstance(XMFLOAT3(1500.0f, terrain->GetHeight(1500.0f, 200.0f), 200.0f), 
		XMFLOAT3(0.0f, 0.0f, 0.0f), houseScale, 
		"House Demo Scene Environment Map");

	houseModels->CreateInstance(XMFLOAT3(1900.0f, terrain->GetHeight(1900.0f, 200.0f), 200.0f), 
		XMFLOAT3(0.0f, 0.0f, 0.0f), houseScale, 
		"House Demo Scene Parallax Occlusion Mapping");
}

void GameScene::LoadPlayer()
{
	//Alloc memory for player and init (load model)
	playerModel = new Player();
	playerModel->Init(sharedGraphicsInterface->GetDevice(), 
		XMFLOAT3(100.0f, terrain->GetHeight(100.0f, 50.0f), 50.0f), 
		XMFLOAT3(0.0f, 0.0f, 0.0f), 
		XMFLOAT3(0.004f, 0.004f, 0.004f), 
		camera, terrain);
}

void GameScene::LoadNPC()
{
	// Alloc memory for NPC
	NPCModel = new QuestGiver();

	// Load the model: position(0,0,0) for test purposes
	NPCModel->Intialise(sharedGraphicsInterface->GetDevice(),
		XMFLOAT3(20.0f, terrain->GetHeight(20.0f, 400.0f), 400.0f), 
		XMFLOAT3(MathHelper::ToRadians(-90.0), MathHelper::ToRadians(180.0f), 0.0f),
		XMFLOAT3(0.04f, 0.04f, 0.04f),
		terrain);
}

void GameScene::InitLighting()
{
	//Set scene lighting. Randomly place lights in a grid: 40x10x60
	//Done using new to avoid stack overflow issues when we have > 3000 lights
	//or so.
	TileBasedLight* lightArr = new TileBasedLight[NUM_LIGHTS];
	//Loop through lights and set properties.
	for (UINT i = 0; i < NUM_LIGHTS; i++)
	{
		//No colour output. 
		XMFLOAT4 null = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
		//Random diffuse and spec colour.
		XMFLOAT4 diffSpecCol = XMFLOAT4(MathHelper::RandF(), MathHelper::RandF(), 
			MathHelper::RandF(), 1.0f);
		//Ambient term
		XMFLOAT4 amb = XMFLOAT4(diffSpecCol.x/5.0f, diffSpecCol.y/5.0f, 
			diffSpecCol.z/5.0f, 1.0f);
		//Random location.
		XMFLOAT3 randLoc = XMFLOAT3(MathHelper::RandF(0.0f, 40.0f),
			MathHelper::RandF(1.0f, 10.0f), MathHelper::RandF(-5.0f, 55.0f));

		//Randon range.
		float randRange = MathHelper::RandF(1.0f, 5.0f);
		//Create point lights. 
		lightArr[i].InitPointLight(amb, diffSpecCol, diffSpecCol, 
			randLoc, randRange);
	}

	//Set scene lighting - creating our required buffers - we want the option
	//to visualise our lights, so set the required bool to true.
	sharedGraphicsInterface->SetSceneLights(&lightArr[0], NUM_LIGHTS, true);

	//Delete dynamically allocated light array.
	delete []lightArr;
}

void GameScene::InitDirecLight()
{
	//Set Directional light. 
	dLight.Ambient = XMFLOAT4(0.06f, 0.06f, 0.06f, 1.0f);
	dLight.Diffuse = XMFLOAT4(0.6f, 0.6f, 0.6f, 1.0f);
	dLight.Specular = XMFLOAT4(0.6f, 0.6f, 0.6f, 64.0f);
	dLight.Direction = XMFLOAT3(0.75f, -0.32f, +1.0f);

	//Set single directional light 
	sharedGraphicsInterface->SetDirectionalLights(&dLight, 1, 0);
}

void GameScene::InitHemisphericalAmbientLight()
{
	//White top (Sky), green bottom. (grass)
	sharedGraphicsInterface->SetHemisphericAmbientColours(XMFLOAT3(0.376f, 0.466f, 0.251f), 
		XMFLOAT3(1.0f, 1.0f, 1.0f));
}

void GameScene::OnMouseDown(WPARAM btnState, int x, int y, UINT originalMessage)
{
	AbstractScene::OnMouseDown(btnState, x, y, originalMessage);
}

void GameScene::OnMouseUp(WPARAM btnState, int x, int y, UINT originalMessage)
{
	//Toggle free camera if right mouse button is released (after a press)
	if (originalMessage == WM_RBUTTONUP)
		useFreeCamera = !useFreeCamera;
}

void GameScene::OnMouseMove(WPARAM btnState, int x, int y)
{
	AbstractScene::OnMouseMove(btnState, x, y);
}

void GameScene::OnResize()
{
	AbstractScene::OnResize();
}

void GameScene::UpdateWithDelta(float delta)
{
	//update all objects in the scene
	ObjectManager::GetInstance()->Update(delta);

	//check object collisions
	ObjectManager::GetInstance()->CheckCollisions();


	//Update the terrain (animate waves)
	terrain->Update(delta);

	//Update single directional light 
	sharedGraphicsInterface->SetDirectionalLights(&dLight, 1, 0);

	//Decide what to render?
	if( GetAsyncKeyState('1') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SCENE; 
	if( GetAsyncKeyState('2') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_Z_PRE_PASS_DEPTH_BUFFER;
	if ( GetAsyncKeyState('3') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_MAX_Z_DEPTH_BUFFER;
	if ( GetAsyncKeyState('4') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_LLSEB_GREYSCALE;
	if ( GetAsyncKeyState('5') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_LLSEB_COLOUR;
	if ( GetAsyncKeyState('6') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_LLSEBT_GREYSCALE;
	if ( GetAsyncKeyState('7') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_LLSEBT_COLOUR;
	if ( GetAsyncKeyState('8') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SSAO_VIEW_NORMALS;
	if ( GetAsyncKeyState('9') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SSAO_VIEW_DEPTHS;
	if ( GetAsyncKeyState('0') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SSAO_MAP;
	if ( GetAsyncKeyState('L') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SHADOW_DEPTH;
	if ( GetAsyncKeyState('H') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_TERRAIN_REFLECTION;
	if ( GetAsyncKeyState('J') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_TERRAIN_REFACTION;

	//Render the lights and HUD?
	if ( GetAsyncKeyState('G') & 0x8000 )
		shouldRenderLights = true;
	if ( GetAsyncKeyState('F') & 0x8000 )
		shouldRenderLights = false;

	//Check for transition to another scene.
	//
	//First, are we within range if a house - get the key of the scene to transition too. We 
	//can then ask the SceneManager to move us to that scene!
	bool inRangeOfHouse = false;
	std::string sceneKeyForTransition = "";
	houseModels->IsHouseWithinRange(camera->GetPosition(), sceneKeyForTransition, inRangeOfHouse);

	//If we are in range of house and we have pressed 'E' to enter, then transition to the scene.
	if ( (inRangeOfHouse) && (GetAsyncKeyState('E') & 0x8000) )
	{
		//Debug output - what scene are we moving too
		OutputDebugStringW(L"In range of a house. Will transitioning to scene with Key: ");
		OutputDebugStringA(sceneKeyForTransition.c_str());
		OutputDebugStringW(L"\n");

		//Ask scene manager to begin the transition process - which for us, will
		//be a simple unload of game scene and load of the demo scene. Note, we will Load data
		//for the new scene but do NOT unload this scene
		SceneManager::GetInstance()->TransitionToSceneWithKey(sceneKeyForTransition, false, true);
	}

	//camera->GetCameraRotationDegreesPitch();
	camera->GetCameraRotationDegreesYaw();
}

void GameScene::RenderScene()
{

	//Start rendering - Clear backbuffer and depth stencil view.
	float clearColGrey[4] = {0.6f, 0.6f, 0.6f, 1.0f};
	sharedGraphicsInterface->StartFrame(clearColGrey, camera);

	//Add models to the batch.
	//
	//Terrain:
	sharedGraphicsInterface->AddModelToBatch((AbstractAdvancedModel*)terrain);

	// Add all models to batch
	ObjectManager::GetInstance()->AddAllToBatch(sharedGraphicsInterface);

	//Houses.
	houseModels->AddToBatch(sharedGraphicsInterface);

	//Render the 3D scene.
	sharedGraphicsInterface->Render(shouldRenderLights);

	// Draw the game HUD if it is toggled on
	// Begin 2D rendering (Text/HUD)
	sharedGraphicsInterface->Begin2DRendering();
	ObjectManager::GetInstance()->HUDRender(sharedGraphicsInterface);

	//End of text rendering
	sharedGraphicsInterface->End2DRendering();

	//Draw depth buffer (filled from Z Pre Pass).
	if (sceneState == SCENE_STATE_DRAW_Z_PRE_PASS_DEPTH_BUFFER)
		sharedGraphicsInterface->DrawZPrePassFilledDepthBuffer();
	//Draw max tile depth texture.
	else if (sceneState == SCENE_STATE_DRAW_CS_MAX_Z_DEPTH_BUFFER)
		sharedGraphicsInterface->DrawTileMaxZBuffer(); 
	else if (sceneState == SCENE_STATE_DRAW_CS_LLSEB_GREYSCALE)
		sharedGraphicsInterface->DrawLightListStartEndBufferGreyscale();
	else if (sceneState == SCENE_STATE_DRAW_CS_LLSEB_COLOUR)
		sharedGraphicsInterface->DrawLightListStartEndBufferColour();
	else if (sceneState == SCENE_STATE_DRAW_CS_LLSEBT_GREYSCALE)
		sharedGraphicsInterface->DrawLightListStartEndBufferTransparantGreyscale();
	else if (sceneState == SCENE_STATE_DRAW_CS_LLSEBT_COLOUR)
		sharedGraphicsInterface->DrawLightListStartEndBufferTransparantColour();
	else if (sceneState == SCENE_STATE_DRAW_SSAO_VIEW_NORMALS)
		sharedGraphicsInterface->DrawSSAOViewNormals();
	else if (sceneState == SCENE_STATE_DRAW_SSAO_VIEW_DEPTHS)
		sharedGraphicsInterface->DrawSSAOViewDepths();
	else if (sceneState == SCENE_STATE_DRAW_SSAO_MAP)
		sharedGraphicsInterface->DrawSSAOMap();
	else if (sceneState == SCENE_STATE_DRAW_SHADOW_DEPTH)
		sharedGraphicsInterface->DrawShadowMapDepth();
	else if (sceneState == SCENE_STATE_DRAW_TERRAIN_REFLECTION)
		terrain->DebugDrawReflectionTexture(sharedGraphicsInterface->GetContext());
	else if (sceneState == SCENE_STATE_DRAW_TERRAIN_REFACTION)
		terrain->DebugDrawRefactionTexture(sharedGraphicsInterface->GetContext());

	//Present the backbuffer (if in full screen, present with vsync on)
	bool fsState = sharedGraphicsInterface->IsInFullScreenMode();
	sharedGraphicsInterface->EndFrame(fsState);
}